/*
TG DPS.  Simulate warrior dps with titan's grip.
Copyright (C) 2008 David Warburton

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
#include "common.h"
#include "abilities.h"


int roll(int min, float max) {
	return min + (int) ((max-min) * (rand() / (RAND_MAX + 1.0)));
}
void sleep(unsigned int mseconds)
{
	clock_t goal = mseconds + clock();
	while (goal > clock());
}

int verbose;

player::player (int a, int c) {
	flurry_charges = 0;
	mtick = 0;
	otick = 0;

	rampage_avail = 0;
	rampage_crit_bonus = 0;

	slam_avail = 0;
	ap = a; crit = c; 
	WW_cd = BT_cd = gcd = 0;
	rage = 15;
	HS = false;
	starve = 0;
	haste_mod = 1;
	hit_rating = 0;  //32.8 per 1%

}
int player::get_ap()
{
	return ap;
}
int player::get_crit()
{
	return crit + rampage_crit_bonus;
}
int player::get_miss(int ab)
{
	bool tg = main->twohands || off->twohands;
	int base = 0;
	switch (ab)
	{
	case AB_AUTO:
		if (HS)
			base = tg ? 23 : 8;
		else
			base = 28;
		break;
	case AB_WW:
	case AB_BT:
	case AB_SLAM:
		base = tg ? 23 : 8;
		break;
	default:
		base = 8;
	}
	return base - (int)((float)hit_rating/32.8);
}

void player::model(int duration) // duration in deciseconds (i.e. a value of 300 is 30 seconds)
{
	int now = 0;
	int mspeed, ospeed, dps;
	long long dmg;
	double f = 0.0;
	int tot_ww_dmg = 0;
	int tot_bt_dmg = 0;
	int tot_slam_dmg = 0;
	int i_starve = 0;
	int ramps_up = 0;
	int flurry_up = 0;
	int rage_lost = 0;
	int rage_made = 0;
	int rt = 0;
	hit_type results;
	starve = 0;
	rage = 15;
	dps = 0;

	

	if (!main)
		return;
	if (!off)
		return;
	//calculate modified speeds
	mspeed =  (int)(main->speed / haste_mod );
	ospeed =  (int)(off->speed / haste_mod ) ;

	//we use a tick timer for each hand.  Reset it to the calculated speed above

	//begin the fight swinging
	mtick = 0;
	otick = 0;

	if (verbose) printf ("mspeed: %d, ospeed: %d\n", mspeed, ospeed);
	do
	{
		//main hand hit
		if (mtick)
			mtick--;
		else
		{
			if (rage > 50)
			{
				HS = true;
				rage -= ab_table[AB_HS].rage_cost;
			}
			if (flurry_charges)
				flurry_charges--;
			
			dmg = autoattack(main, &results);
			dps += dmg;
			
			switch (results)
			{
				case MISS:
					if (verbose & OUT_STORY) printf("%-7.7d: main %s and misses\n", now, HS ? "Heroic Strikes" : "swings");
					f = 0;
					break;
				case HIT:
					if (verbose & OUT_STORY) printf("%-7.7d: main %s and hits for %d\n", now,HS ? "Heroic Strikes" : "swings", dmg);
					f = 3.5;
					break;
				case CRIT:
					if (verbose & OUT_STORY) printf("%-7.7d: main %s and CRITS << %d >>\n", now,HS ? "Heroic Strikes" : "swings", dmg);
					f = 7;
					break;
			}
			if (HS)
			{
				HS = false;
			}
			else
			{
				rt = (int)(15*dmg / (4*274) + f * main->speed / 20);
				rage += rt;
				rage_made += rt;
			}
			if ((verbose & OUT_RAGE) && f) printf("%-7.7d: Rage at %d\n",now, rage);
			if (rage > 100)
			{
				rage_lost += (rage - 100);
				if (verbose & OUT_RAGE) printf ("%-7.7d: lost %d rage.\n", now, rage - 100);
				rage = 100;
			}
			if (flurry_charges)
				mtick = (int)(mspeed * 0.75);
			else
				mtick = mspeed;
		}
		//offhand hit
		if (otick)
			otick--;
		else
		{
			dmg = autoattack(off, &results);
			dmg = (int)((float)dmg * .5 * 1.25);
			dps += dmg;
			
			if (flurry_charges)
				flurry_charges--;

			switch (results)
			{
				case MISS:
					if (verbose & OUT_STORY) printf("%-7.7d: off  swings and misses\n", now);
					f = 0;
					break;
				case HIT:
					if (verbose & OUT_STORY) printf("%-7.7d: off  swings and hits for %d\n", now, dmg);
					f = 1.75;
					break;
				case CRIT:
					if (verbose & OUT_STORY) printf("%-7.7d: off  swings and CRITS << %d >>\n", now, dmg);
					f = 3.5;
					break;
			}

			rt = (int)(15*dmg / (4*274) + f * off->speed / 20);
			rage += rt;
			rage_made += rt;

			if (verbose & OUT_RAGE) printf("%-7.7d: Rage at %d\n",now, rage);
			if (rage > 100)
			{
				rage_lost += (rage - 100);
				if ((verbose & OUT_RAGE) && f) printf ("%-7.7d: lost %d rage.\n", now, rage - 100);
				rage = 100;
			}
			if (flurry_charges)
				otick = (int)(ospeed * 0.75);
			else
				otick = ospeed;
		}
		//White hits are over

		//gcd
		if (gcd)
			gcd--;

		//Rampage stuff
		if (rampage_avail)
		{
			rampage_avail --;

			if (!rampage_avail)
			{
				if (verbose & OUT_RAMPAGE) printf("%-7.7d: Rampage expired.\n", now);
				rampage_crit_bonus = 0;
			}
			else
				ramps_up++;
		}

		//slam
		if (slam_avail)
		{
			if (!gcd && rage > 50)
			{
				dmg = do_slam(now);
				dps += dmg;
				tot_slam_dmg += dmg;
			}
			slam_avail--;
		}
		
		//WW and BT
		i_starve = 0;


		if (WW_cd)
			WW_cd -= 1;
		else if (!gcd && rage >= 25)
		{
			dmg = do_WW(now);
			dps += dmg;
			tot_ww_dmg += dmg;
		}
		else
			i_starve = gcd ? 0 : 1;

		if (BT_cd)
			BT_cd --;
		else if (!gcd && rage >= 30)
		{
			int i = do_BT(now);
			tot_bt_dmg += i;
			dps += i;
		}
		else
			i_starve = gcd ? 0 : 1;

		if (i_starve)
		{
			starve ++;
			if (verbose & OUT_STARVE) printf("%-7.7d: Starving...\n", now);
		}

		//count flurry uptime
		if (flurry_charges)
			flurry_up++;

		now++;
		//sleep(100);  // enable for realtime simulation
	} while (now < duration);
	printf ("damage was: %d and DPS was: %d\n", dps, dps * 10 / now);
	printf("I had Rage per second of %d for the fight.\n", rage_made*10/now);
	if (starve)
		printf("I was rage starved for %d deciseconds\n", starve);
	if (rage_lost)
		printf("I lost %d rage when i was rage capped\n", rage_lost);
	printf("I did %d Heroic Strikes.\n", ab_table[AB_HS].swing_count);
	printf("I had a rate of %.2d%% rampage uptime\n", ramps_up*100/now);
	printf("I had a flurry uptime of %.2d%%.\n", flurry_up*100/now);
	printf("I had %d slams for an average of %d resulting in %d dps.\n", ab_table[AB_SLAM].swing_count, tot_slam_dmg/(ab_table[AB_SLAM].swing_count - ab_table[AB_SLAM].miss_count), tot_slam_dmg*10/now);
	printf("avg ww dmg was: %d out of %d ww's resulting in %d dps.\n", tot_ww_dmg/(ab_table[AB_WW].swing_count - ab_table[AB_WW].miss_count), ab_table[AB_WW].swing_count, tot_ww_dmg*10/now);
	printf("Miss results:\n");
	for (int i = 0; i < AB_MAX; i++)
	{
		if (ab_table[i].swing_count > 0)
			printf("   %-15.15s %.2d%%\n", ab_table[i].name, ab_table[i].miss_count*100/ab_table[i].swing_count);
	
		ab_table[i].swing_count = 0;
		ab_table[i].miss_count = 0;
	}
}



int main(int argc, char ** argv) {
	
	//verbose =  OUT_RAMPAGE | OUT_RAGE | OUT_STORY | OUT_STARVE;

	verbose = 0;
	srand( (unsigned)time( NULL ) );

	/*
		http://wotlk.wowhead.com/?item=39245
		Demise x2
	*/
	WEAPON twohand = new weapon(522, 784, 35, true);
	
	/*
		http://wotlk.wowhead.com/?item=39291
		Torment of the Banished x2
	*/
	WEAPON onehand = new weapon(261, 485, 26, false);
	PLAYER p = new player(6000, 30);
	
	int runtime = 72000; //10 hour
	//int runtime = 600; //10 minutes	
	printf("Titan's Grip\n");
	p->main = p->off = twohand;
	p->hit_rating = 90;
	p->model(runtime);
	printf("Old style\n");
	p->main = p->off = onehand;
	p->hit_rating = 0;
	p->model(runtime);

	return 0;
}
